﻿using System;
using System.Collections.Generic;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Data;
using Microsoft.Practices.EnterpriseLibrary.Data.Configuration;
using Sucdri.Infrastructure.Core.Context;

namespace Sucdri.Infrastructure.Core.DbUtility
{
   public static class DbFactory
   {
      private const string KeySuffix = "@common.dbutility.dataaccess.context";

      private static readonly Dictionary<string, Database> DatabaseLookup =
         new Dictionary<string, Database>(StringComparer.InvariantCultureIgnoreCase);

      private static readonly string DefaultDatabaseName =
         DatabaseSettings.GetDatabaseSettings(ConfigurationSourceFactory.Create()).DefaultDatabase;

      private static readonly object LockObject = new object();

      private static Database GetDatabase(string dbName)
      {
         if (!DatabaseLookup.ContainsKey(dbName))
         {
            lock (LockObject)
            {
               if (!DatabaseLookup.ContainsKey(dbName))
                  DatabaseLookup.Add(dbName, DatabaseFactory.CreateDatabase(dbName));
            }
         }
         return DatabaseLookup[dbName];
      }

      public static DbHelper GetDbHelper(string dbName)
      {
         Guard.ArgumentNotNullOrEmpty("dbName", dbName);
         string key = GetKey(dbName);
         if (ServiceContext.Current.Contains(key))
         {
            var dataAccessContext = ServiceContext.Current[key] as DataAccessContext;
            Guard.VariableNotNull("dataAccessContext", dataAccessContext);
            return new DbHelper(dataAccessContext.Database, dataAccessContext.Transaction);
         }
         return new DbHelper(GetDatabase(dbName));
      }

      public static DbHelper GetDbHelper()
      {
         return GetDbHelper(DefaultDatabaseName);
      }

      private static string GetKey(string dbName)
      {
         return dbName + KeySuffix;
      }

      internal static void BeginTransaction()
      {
         BeginTransaction(DefaultDatabaseName);
      }

      internal static void BeginTransaction(string dbName)
      {
         Guard.ArgumentNotNullOrEmpty("dbName", dbName);

         string key = GetKey(dbName);
         if (ServiceContext.Current.Contains(key))
         {
            throw new InvalidOperationException(string.Format("数据库{0}的上下文环境已经存在。", dbName));
         }
         var dataAccessContext = new DataAccessContext(GetDatabase(dbName));
         ServiceContext.Current.Add(key, dataAccessContext);
         dataAccessContext.BeginTransaction();
      }

      internal static void Rollback()
      {
         Rollback(DefaultDatabaseName);
      }

      internal static void Rollback(string dbName)
      {
         Guard.ArgumentNotNullOrEmpty("dbName", dbName);

         string key = GetKey(dbName);
         if (!ServiceContext.Current.Contains(key))
         {
            throw new InvalidOperationException(string.Format("数据库{0}的上下文环境不存在。", dbName));
         }

         var dataAccessContext = ServiceContext.Current[key] as DataAccessContext;
         Guard.VariableNotNull("dac", dataAccessContext);
         try
         {
            dataAccessContext.Rollback();
         }
         finally
         {
            dataAccessContext.CloseConnection();
            ServiceContext.Current.Remove(key);
         }
      }

      internal static void Commit()
      {
         Commit(DefaultDatabaseName);
      }

      internal static void Commit(string dbName)
      {
         Guard.ArgumentNotNullOrEmpty("dbName", dbName);
         string key = GetKey(dbName);
         if (!ServiceContext.Current.Contains(key))
            throw new InvalidOperationException(string.Format("数据库{0}的上下文环境不存在。", dbName));
         var dataAccessContext = ServiceContext.Current[key] as DataAccessContext;
         Guard.VariableNotNull("dac", dataAccessContext);
         try
         {
            dataAccessContext.Commit();
         }
         finally
         {
            dataAccessContext.CloseConnection();
            ServiceContext.Current.Remove(key);
         }
      }

   }
}